﻿using System;
using Microsoft.Popfly.GameCreator.Data;
using System.Collections.Generic;

namespace Microsoft.Popfly.GameCreator.GameEngine.Events
{
    public static class PropertyEventFactory
    {
        public static IEventChecker CreatePropertyEventChecker(Game game, BehaviorOwner owner, EventInfo eventInfo)
        {
            if (eventInfo.GetType() != typeof(PropertyChangeEventInfo))
            {
                throw new ArgumentException(
                    Utils.ErrorMessagePrefix +
                    "CreatePropertyEventChecker only accepts EventInfos of type PropertyChangeEventInfo.  " +
                    "eventInfo's type was '" +
                    eventInfo.GetType().ToString() +
                    "'.");
            }

            PropertyChangeEventInfo propertyChangeInfo = (PropertyChangeEventInfo)eventInfo;

            if (propertyChangeInfo.PropertyName == null)
            {
                throw new ArgumentException(
                    "Cannot create a property change event without a property name.");
            }

            string actorOrInstanceName = null;
            if (propertyChangeInfo.Includes != null)
            {
                System.Diagnostics.Debug.Assert(propertyChangeInfo.Includes.Count <= 1);
                if (propertyChangeInfo.Includes.Count == 1)
                {
                    actorOrInstanceName = propertyChangeInfo.Includes.getIndexedKey(0);
                }
            }

            PropertyHolder propertyHolder = null;

            switch (propertyChangeInfo.SubType)
            {
                case "self" :
                    propertyHolder = owner;
                    break;
                case "scene" :
					if (actorOrInstanceName != null && actorOrInstanceName != owner.Scene.Name)
                    {
                        throw new ArgumentException(
                            "Can't monitor properties on scenes other than the current one.\n" +
                            "CurrentScene: '" + owner.Scene.Name + "'.\n" +
                            "Property Holder: '" + actorOrInstanceName + "'."
                            );
                    }
                    else
                    {
                        propertyHolder = owner.Scene;
                    }
                    break;
                case "game" :
                    propertyHolder = game;
                    break;
                case "actorInstance" :
                    propertyHolder = owner.Scene.GetActor(actorOrInstanceName);
                    if (propertyHolder == null)
                    {
                        throw new ArgumentException(
                            "Can't monitor actor instance '" + actorOrInstanceName + "' because it doesn't exist");
                    }
                    break;
            }

            if (propertyHolder != null)
            {
                switch (propertyChangeInfo.PropertyCondition)
                {
                    case null:
                        return new PropertyChangeEventChecker(game, owner, propertyHolder, propertyChangeInfo.PropertyName);
                    case "GreaterThan":
                        return new PropertyThresholdEventChecker(game, owner, propertyHolder, propertyChangeInfo.PropertyName, propertyChangeInfo.PropertyValue, PropertyCondition.ConditionType.GREATERTHAN);
                    case "GreaterThanOrEqual":
                        return new PropertyThresholdEventChecker(game, owner, propertyHolder, propertyChangeInfo.PropertyName, propertyChangeInfo.PropertyValue, PropertyCondition.ConditionType.GREATERTHANOREQUAL);
                    case "LessThan":
                        return new PropertyThresholdEventChecker(game, owner, propertyHolder, propertyChangeInfo.PropertyName, propertyChangeInfo.PropertyValue, PropertyCondition.ConditionType.LESSTHAN);
                    case "LessThanOrEqual":
                        return new PropertyThresholdEventChecker(game, owner, propertyHolder, propertyChangeInfo.PropertyName, propertyChangeInfo.PropertyValue, PropertyCondition.ConditionType.LESSTHANOREQUAL);
                    case "Equals":
                        return new PropertyThresholdEventChecker(game, owner, propertyHolder, propertyChangeInfo.PropertyName, propertyChangeInfo.PropertyValue, PropertyCondition.ConditionType.EQUALS);
                    default:
                        throw new NotImplementedException(
                            Utils.ErrorMessagePrefix +
                            "CreatePropertyEventChecker doesn't know how to handle a property condition of type '" +
                            propertyChangeInfo.PropertyCondition + "'.");
                }
            }
            else
            {
                switch (propertyChangeInfo.PropertyCondition)
                {
                    case null:
                        return new MultiActorPropertyChangeEventChecker(game, owner, actorOrInstanceName, propertyChangeInfo.PropertyName);
                    case "GreaterThan":
                        return new MultiActorPropertyThresholdEventChecker(game, owner, actorOrInstanceName, propertyChangeInfo.PropertyName, propertyChangeInfo.PropertyValue, PropertyCondition.ConditionType.GREATERTHAN);
                    case "GreaterThanOrEqual":
                        return new MultiActorPropertyThresholdEventChecker(game, owner, actorOrInstanceName, propertyChangeInfo.PropertyName, propertyChangeInfo.PropertyValue, PropertyCondition.ConditionType.GREATERTHANOREQUAL);
                    case "LessThan":
                        return new MultiActorPropertyThresholdEventChecker(game, owner, actorOrInstanceName, propertyChangeInfo.PropertyName, propertyChangeInfo.PropertyValue, PropertyCondition.ConditionType.LESSTHAN);
                    case "LessThanOrEqual":
                        return new MultiActorPropertyThresholdEventChecker(game, owner, actorOrInstanceName, propertyChangeInfo.PropertyName, propertyChangeInfo.PropertyValue, PropertyCondition.ConditionType.LESSTHANOREQUAL);
                    case "Equals":
                        return new MultiActorPropertyThresholdEventChecker(game, owner, actorOrInstanceName, propertyChangeInfo.PropertyName, propertyChangeInfo.PropertyValue, PropertyCondition.ConditionType.EQUALS);
                    default:
                        throw new NotImplementedException(
                            Utils.ErrorMessagePrefix +
                            "CreatePropertyEventChecker doesn't know how to handle a property condition of type '" +
                            propertyChangeInfo.PropertyCondition + "'.");
                }

            }
        }
    }
}
